/*******************************************************************************
 * Venice Noise Android Application
 * Copyright (C) 2011  Worcester Polytechnic Institute, Wesley Ripley
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; version 2
 * of the License
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 * 
 * If you plan to use or modify any of this code please contact Fabio Carrera (carrera.fabio@gmail.com)
 * Also please attribute any code used in the final product to the developers. 
 * 
 * Author: Wesley Ripley (wripley@wpi.edu) 
 *******************************************************************************/
package org.venicenoise.app;

import java.io.File;
import java.util.Map;
import java.util.TreeMap;

import org.venicenoise.app.location.LocationService;
import org.venicenoise.app.location.StaticLocationService;


import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.location.Location;
import android.util.Log;

/**
 * The data structure that stores data that has been queued.
 * @author Wesley Ripley
 * @version 12/15/2011
 */
public class SubmissionQue {
	//constants identifying the database
    private static final String DATABASE_NAME = "noisDataUploadQue";
    private static final String DATABASE_TABLE = "noiseData";
    /**
     * The version of the database. If there are any changes increment this value.
     */
    public static final int DATABASE_VERSION = 4;
    //constants for the keys where everything is stored in the database
    private static final String KEY_ID = "_id";
    private static final String KEY_TYPE = "type";
    private static final String KEY_LAT = "latitude";
    private static final String KEY_LONG = "longitude";
    private static final String KEY_ACCURACY = "accuracy";
    private static final String KEY_BEARING = "bearing";
    private static final String KEY_SPL = "spl";
    private static final String KEY_LEG = "leq";
    private static final String KEY_SEL = "sel";
    private static final String KEY_DESC = "description";
    private static final String KEY_TIME = "timestamp";
    private static final String KEY_FILE = "audioFile";
    private static final String KEY_PHOTO = "photoFile";
    
    /**
     * Database creation SQL statement
     * If this is changed, make sure you increment DATABASE_VERSION
     */
    private static final String DATABASE_CREATE =
    	"CREATE TABLE IF NOT EXISTS noiseData (" + 
    			  KEY_ID + " integer primary key autoincrement NOT NULL, " + 
    			  KEY_TYPE + " smallint NOT NULL, " +
    			  KEY_LAT + " double precision NOT NULL, " +
    			  KEY_LONG + " double precision NOT NULL, " +
    			  KEY_ACCURACY + " float NOT NULL, " +
    			  KEY_BEARING + " float NOT NULL, " +
    			  KEY_SPL + " double precision NOT NULL, " +
    			  KEY_LEG + " double precision NOT NULL, " +
    			  KEY_SEL + " double precision NOT NULL, " +
    			  KEY_DESC + " text NOT NULL, " +
    			  KEY_FILE + " text NOT NULL, " +
    			  KEY_PHOTO + " text NOT NULL, " +
    			  KEY_TIME + " long NOT NULL);";
    
    private final Context context;
    private final DatabaseHelper dbHelper;
    private final SQLiteDatabase database;
    
    /**
     * Constructor for SubmissionQueue
     * @param context
     * 		the context of the application
     */
	public SubmissionQue(Context context)
	{
		this.context = context;
		dbHelper = new DatabaseHelper(this.context);
		database = dbHelper.getWritableDatabase();
	}
	
    /**
     * Create a new submission with the data provided. If the entry is
     * successfully created return the new rowId for that entry, otherwise return
     * a -1 to indicate failure.
     * 
     * @param data
     * 		the collected noise data
     * @return 
     * 		rowId or -1 if failed
     */
	public long addSubmission(NoiseData data)
	{
		ContentValues initialValues = new ContentValues();
		//fill every value from the given data
		Location location = data.getLocationService().getLocation();
        initialValues.put(KEY_LAT,location.getLatitude());
        initialValues.put(KEY_LONG, location.getLongitude());
        initialValues.put(KEY_ACCURACY, location.getAccuracy());
        initialValues.put(KEY_BEARING, location.getBearing());
        initialValues.put(KEY_SPL, data.getSPL());
        initialValues.put(KEY_LEG,data.getLeq());
        initialValues.put(KEY_SEL,data.getSEL());
        initialValues.put(KEY_DESC, data.getDescription());
        if(data.getAudioFile() != null)
        {
        	initialValues.put(KEY_FILE, data.getAudioFile().getAbsolutePath());
        }
        if(data.getPhotoFile() != null)
        {
        	initialValues.put(KEY_PHOTO,data.getPhotoFile().getAbsolutePath());
        }
        else
        {
        	initialValues.put(KEY_PHOTO,"");
        }
        initialValues.put(KEY_TIME, data.getTimestamp());
        initialValues.put(KEY_TYPE, data.getType().getValue());
        //insert values into table
        return database.insert(DATABASE_TABLE, null, initialValues);
	}
	
    /**
     * Delete the submission with the given rowId
     * 
     * @param rowId id of note to delete
     * @return true if deleted, false otherwise
     */
    public boolean deleteSubmission(long rowId) {
        return database.delete(DATABASE_TABLE, KEY_ID + "=" + rowId, null) > 0;
    }
    
    /**
     * Get the entire queue.
     * @return
     * 		A map representing the entire queue. 
     * 		The keys in this map are the row id's for each piece of data.
     */
    public Map<Integer,NoiseData> getAll() {
        Cursor cursor = getCursorOverAll();
        Map<Integer, NoiseData> map = new TreeMap<Integer,NoiseData>();
        cursor.moveToFirst();
        //go through each cursor position
        while(!cursor.isAfterLast()) {
        	//convert cursor to noise data  and add it to map
        	NoiseData data = cursorToData(cursor);
        	map.put(cursor.getInt(cursor.getColumnIndex(KEY_ID)), data);
        	cursor.moveToNext();
        }
        cursor.close();
        return map;
    }
    
    /**
     * Return a Cursor over the list of all the submissions in the database
     * 
     * @return 
     * 		Cursor over all submissions
     */
    public Cursor getCursorOverAll() {
        return database.query(DATABASE_TABLE, null, null, null, null, null, null);
    }
    
    /**
     * Close the queue. Call when you are done with the queue
     */
    public void close()
    {
    	database.close();
    	dbHelper.close();
    }
    
    /**
     * Make a cursor into NoiseData.
     * @param cursor
     * 		A cursor over a row in the database. Data comes from the current row.
     * @return
     * 		The noise data created from the cursor
     */
    public static NoiseData cursorToData(Cursor cursor)
    {
    	LocationService location = new StaticLocationService(cursor.getDouble(cursor.getColumnIndex(KEY_LAT)),
    														 cursor.getDouble(cursor.getColumnIndex(KEY_LONG)),
    														 cursor.getFloat(cursor.getColumnIndex(KEY_ACCURACY)),
    														 cursor.getFloat(cursor.getColumnIndex(KEY_BEARING)));
    	File audioFile = new File(cursor.getString(cursor.getColumnIndex(KEY_FILE)));
    	File photoFile;
    	String photoFilePath = cursor.getString(cursor.getColumnIndex(KEY_PHOTO));
    	if(!AppUtil.isEmptyOrNull(photoFilePath))//is there was a photo taken
    	{
    		photoFile = new File(photoFilePath);
    	}
    	else
    	{
    		photoFile = null;
    	}
    	return new NoiseData(location,cursor.getDouble(cursor.getColumnIndex(KEY_SPL)),
    								  cursor.getDouble(cursor.getColumnIndex(KEY_LEG)),
    								  cursor.getDouble(cursor.getColumnIndex(KEY_SEL)),
    								  cursor.getString(cursor.getColumnIndex(KEY_DESC)),
    								  audioFile, photoFile,
    								  cursor.getLong(cursor.getColumnIndex(KEY_TIME)),
    								  NoiseData.Type.typeFromValue(cursor.getShort(cursor.getColumnIndex(KEY_TYPE))));
    	
    }
    
    /**
     * Get the data most recently queued in the form of a map with one item
     * @return
     * 		A map containing a single pair where the key is the row id of the last submitted data point
     * 		and the value is the data last submitted
     */
    public Map<Integer,NoiseData> getLastSubmittedAsMap()
    {
        Cursor cursor = getCursorOverAll();
        if(cursor != null)
        {
                if(cursor.moveToLast())
                {
                        NoiseData data = cursorToData(cursor);
                        Map<Integer,NoiseData> map = new TreeMap<Integer,NoiseData>();
                        map.put(cursor.getInt(cursor.getColumnIndex(KEY_ID)), data);
                        cursor.close();
                        return map;
                }
                cursor.close();
        }
        return null;
    }
    
    /**
     * Get the data most recently queued
     * @return
     * 		The data most recently queued
     */
    public NoiseData getLastSubmitted()
    {
    	Map<Integer,NoiseData> lastSubmittedMap = getLastSubmittedAsMap();
    	if(lastSubmittedMap == null)
    	{
    		return null;
    	}
    	return lastSubmittedMap.values().iterator().next();
    }
	
    /**
     * A helper class for creating the database
     * @author Wesley Ripley
     * @version 12/15/2011
     *
     */
    private static class DatabaseHelper extends SQLiteOpenHelper {

    	/**
    	 * Constructor for DatabaseHelper
    	 * @param context
    	 * 		The context of the application
    	 */
        public DatabaseHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        /**
         * Called when the database needs to be created
         */
        @Override
        public void onCreate(SQLiteDatabase db) {

            db.execSQL(DATABASE_CREATE);
        }

        /**
         * Called when the database needs to be upgraded
         */
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        	if(newVersion > oldVersion)
        	{
        		//TODO: write better upgrades once this is actually live and on the market
        		if(AppUtil.LOGGING)
	            {
        			Log.w(AppUtil.LOG_TAG, "Upgrading database from version " + oldVersion + " to "
	                    + newVersion + ", which will destroy all old data");
	            }
	            db.execSQL("DROP TABLE IF EXISTS " + DATABASE_TABLE);
	            onCreate(db);
        	}
        }
    }

}


